// 这一层曾经是对产出做最外层包装的，但其实包装逻辑不应该属于这一层

import * as _ from 'lodash';
import {
  IPublicTypeCompositeArray,
  IPublicTypeCompositeObject,
  IPublicTypeCompositeValue,
  IPublicTypeJSExpression,
  IPublicTypeJSFunction,
  IPublicTypeJSSlot,
} from '@alilc/lowcode-types';
import { CodeGeneratorError, CompositeValueGeneratorOptions, IScope } from '../types';
import { generateJsSlot } from './jsSlot';
import { generateExpression, generateFunction, getArrowFunction } from './jsExpression';
import { executeFunctionStack } from './aopHelper';
import { parseExpressionGetKeywords } from './expressionParser';
import { isJSExpressionFn } from './common';
import { isJSExpression, isJSFunction, isJSSlot } from '@alilc/lowcode-utils';

interface ILegaoVariable {
  type: 'variable';
  value: string;
  variable: string;
}

function isVariable(v: any): v is ILegaoVariable {
  if (_.isObject(v) && (v as ILegaoVariable).type === 'variable') {
    return true;
  }
  return false;
}

interface DataSource {
  type: 'DataSource';
  id: string;
}

/**
 * 判断是否是数据源类型
 */
function isDataSource(v: unknown): v is DataSource {
  return typeof v === 'object' && v != null && (v as Partial<DataSource>).type === 'DataSource';
}

function generateArray(
  value: IPublicTypeCompositeArray,
  scope: IScope,
  options: CompositeValueGeneratorOptions = {},
): string {
  const body = value.map((v) => generateUnknownType(v, scope, options)).join(',');
  return `[${body}]`;
}

function generateObject(
  value: IPublicTypeCompositeObject,
  scope: IScope,
  options: CompositeValueGeneratorOptions = {},
): string {
  if (value.type === 'i18n') {
    // params 可能会绑定变量，这里需要处理下
    if (value.params && typeof value.params === 'object') {
      return `this._i18nText(${generateUnknownType(_.omit(value, 'type'), scope, options)})`;
    }
    return `this._i18nText(${JSON.stringify(_.omit(value, 'type'))})`; // TODO: 优化：这里可以考虑提取成个常量...
  }

  const body = Object.keys(value)
    .map((key) => {
      const propName = JSON.stringify(key);
      const v = generateUnknownType(value[key], scope, options);
      return `${propName}: ${v}`;
    })
    .join(',\n');

  return `{${body}}`;
}

function generateString(value: string): string {
  // 对于包含特殊字符的字符串，我们需要进行适当的转义处理
  // 但是要保持中文字符的原样显示，不进行 Unicode 转义
  return JSON.stringify(value).replace(/\\u([0-9a-fA-F]{4})/g, (_, code) => {
    return String.fromCodePoint(parseInt(code, 16));
  });
}

function generateNumber(value: number): string {
  return String(value);
}

function generateBool(value: boolean): string {
  return value ? 'true' : 'false';
}

function genFunction(
  value: IPublicTypeJSFunction,
  scope: IScope,
  options: CompositeValueGeneratorOptions = {},
): string {
  const globalVars = parseExpressionGetKeywords(value.value);

  // 从options中获取循环变量名称
  const loopItemName = options.loopItemName;
  const loopIndexName = options.loopIndexName;

  if (globalVars.includes('arguments')) {
    return generateFunction(value, scope, {
      isBindExpr: true,
      loopItemName,
      loopIndexName,
    });
  }

  return generateFunction(value, scope, {
    isArrow: true,
    loopItemName,
    loopIndexName,
  });
}

function genJsSlot(
  value: IPublicTypeJSSlot,
  scope: IScope,
  options: CompositeValueGeneratorOptions = {},
) {
  if (options.nodeGenerator) {
    return generateJsSlot(value, scope, options.nodeGenerator);
  }
  return '';
}

function generateUnknownType(
  value: IPublicTypeCompositeValue,
  scope: IScope,
  options: CompositeValueGeneratorOptions = {},
): string {
  if (_.isUndefined(value)) {
    return 'undefined';
  }

  if (_.isNull(value)) {
    return 'null';
  }

  if (_.isArray(value)) {
    if (options.handlers?.array) {
      return executeFunctionStack(value, scope, options.handlers.array, generateArray, options);
    }
    return generateArray(value, scope, options);
  }

  // FIXME: 这个是临时方案
  // 在遇到 type variable 私有类型时，转换为 JSExpression
  if (isVariable(value)) {
    const transValue: IPublicTypeJSExpression = {
      type: 'JSExpression',
      value: value.variable,
    };

    if (options.handlers?.expression) {
      const expression = executeFunctionStack(
        transValue,
        scope,
        options.handlers.expression,
        generateExpression,
        options,
      );
      return expression || 'undefined';
    }
    return generateExpression(transValue, scope);
  }

  if (isJSExpression(value)) {
    if (options.handlers?.expression) {
      return executeFunctionStack(
        value,
        scope,
        options.handlers.expression,
        generateExpression,
        options,
      );
    }
    return generateExpression(value, scope);
  }

  if (isJSFunction(value) || isJSExpressionFn(value)) {
    if (options.handlers?.function) {
      return executeFunctionStack(value, scope, options.handlers.function, genFunction, options);
    }
    return genFunction(value, scope, options);
  }

  if (isJSSlot(value)) {
    if (options.handlers?.slot) {
      return executeFunctionStack(value, scope, options.handlers.slot, genJsSlot, options);
    }
    return genJsSlot(value, scope, options);
  }

  if (isDataSource(value)) {
    return generateUnknownType(
      {
        type: 'JSExpression',
        value: `this.dataSourceMap[${JSON.stringify(value.id)}]`,
      },
      scope,
      options,
    );
  }

  if (_.isObject(value)) {
    if (options.handlers?.object) {
      return executeFunctionStack(value, scope, options.handlers.object, generateObject, options);
    }
    return generateObject(value as IPublicTypeCompositeObject, scope, options);
  }

  if (_.isString(value)) {
    if (options.handlers?.string) {
      return executeFunctionStack(value, scope, options.handlers.string, generateString, options);
    }
    return generateString(value);
  }

  if (_.isNumber(value)) {
    if (options.handlers?.number) {
      return executeFunctionStack(value, scope, options.handlers.number, generateNumber, options);
    }
    return generateNumber(value);
  }

  if (_.isBoolean(value)) {
    if (options.handlers?.boolean) {
      return executeFunctionStack(value, scope, options.handlers.boolean, generateBool, options);
    }
    return generateBool(value);
  }

  throw new CodeGeneratorError('Meet unknown composite value type');
}

// 这一层先不去掉，做冗余，方便后续重构
export function generateCompositeType(
  value: IPublicTypeCompositeValue,
  scope: IScope,
  options: CompositeValueGeneratorOptions = {},
): string {
  // 对于属性值，需要特殊处理循环变量
  const isAttrValue = options.isAttrValue === true;
  const loopItemName = options.loopItemName;
  const loopIndexName = options.loopIndexName;
  const isJSSlotParams = options.isJSSlotParams === true;

  // 如果是JSExpression类型，并且是属性值，需要特殊处理
  if (isJSExpression(value) && isAttrValue) {
    // 检查表达式中是否包含循环变量引用
    const bindings = scope.bindings?.getAllBindings() || [];
    const exprValue = value.value;

    // console.log('【generateCompositeType】处理JSExpression类型属性值:', exprValue);
    // console.log('【generateCompositeType】当前作用域变量:', bindings);

    // 检查是否包含循环变量引用
    const hasLoopVars = bindings.some((binding) => {
      const pattern = new RegExp(`\\b(this\\.)?${binding}\\b`, 'g');
      return pattern.test(exprValue);
    });

    // console.log('【generateCompositeType】是否包含作用域变量引用:', hasLoopVars);

    if (hasLoopVars) {
      // 检查是否是JSSlot参数引用
      if (isJSSlotParams) {
        // console.log('【generateCompositeType】检测到JSSlot参数引用，保留this前缀');
        // 对于JSSlot参数，保留this前缀
        if (options.handlers?.expression) {
          const customOptions = { ...options, preserveThisForSlotParams: true };
          // console.log(
          //   '【generateCompositeType】使用自定义处理器处理表达式，preserveThisForSlotParams=true',
          // );
          return executeFunctionStack(
            value,
            scope,
            options.handlers.expression,
            (val, s) => generateExpression(val, s, { preserveThisForSlotParams: true }),
            customOptions,
          );
        }
        return generateExpression(value, scope, { preserveThisForSlotParams: true });
      } else {
        // 对于循环变量，不保留this前缀
        if (options.handlers?.expression) {
          // 使用自定义处理器，传入preserveThisForLoopVars=false参数
          const customOptions = { ...options, preserveThisForLoopVars: false };
          // console.log(
          // '【generateCompositeType】使用自定义处理器处理表达式 1，preserveThisForLoopVars=false',
          // );
          return executeFunctionStack(
            value,
            scope,
            options.handlers.expression,
            (val, s) => generateExpression(val, s),
            customOptions,
          );
        }
        // console.log('【generateCompositeType】直接使用generateExpression处理表达式');
        return generateExpression(value, scope);
      }
    }
  }

  // 如果是JSExpression类型，需要处理表达式中的循环变量引用
  // 如果是JSFunction类型，需要处理函数中的循环变量引用
  // 注释掉这段有bug的代码，因为它会错误地将包含filter/map的JSExpression当作JSFunction处理
  // if (isJSFunction(value) || isJSExpression(value)) {
  //   if (value.value?.slice(0.1).includes('function(')) {
  //     value.value = getArrowFunction(value.value);
  //     // console.log('value.value: ', value.value);
  //   }
  // }

  if (isJSFunction(value)) {
    if (options.handlers?.function) {
      // 传递循环变量名称给自定义处理器
      const functionOptions = { ...options };
      return executeFunctionStack(
        value,
        scope,
        options.handlers.function,
        genFunction,
        functionOptions,
      );
    }
    // 传递循环变量名称给genFunction
    // console.log('【generateCompositeType】直接使用genFunction处理函数', value);
    return genFunction(value, scope, options);
  }

  // 如果是对象或数组，需要递归处理其中的循环变量引用
  if (_.isObject(value) && !isJSExpression(value) && !isJSFunction(value) && !isJSSlot(value)) {
    // 处理数组
    if (_.isArray(value)) {
      if (options.handlers?.array) {
        return executeFunctionStack(value, scope, options.handlers.array, generateArray, options);
      }
      return generateArray(value, scope, options);
    }

    // 处理对象
    if (options.handlers?.object) {
      return executeFunctionStack(value, scope, options.handlers.object, generateObject, options);
    }
    return generateObject(value as IPublicTypeCompositeObject, scope, options);
  }

  const result = generateUnknownType(value, scope, options);
  return result;
}
